using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using DeploymentWang.Interfaces;
using DeploymentWang.Interfaces.Entities;

namespace DeploymentWang.Core
{
    /// <summary>
    /// 
    /// </summary>
    public abstract class ConfigXmlFileOperationBase : IConfigurationOperation<ConfigurationRequest, ConfigViolations>
    {
        protected abstract ConfigViolations DoOperation(ConfigurationRequest request,
            ConfigSetting setting,
            string targetLocation,
            XmlDocument xmlDoc, 
            XmlNode xmlNode);

        public bool Invoke(ConfigurationRequest request, out ConfigViolations results)
        {
            var xmlDoc = new XmlDocument();
            var currentFile = string.Empty;
            XmlNamespaceManager currentNsm = null;
            
            results = new ConfigViolations();

            foreach (var setting in request.Settings)
            {
                var fileLocation = Path.Combine(request.Request.InstallationFolder,
                                setting.Target);

                if (string.Compare(fileLocation, currentFile, 
                                   StringComparison.InvariantCultureIgnoreCase) != 0)
                {
                    if (!File.Exists(fileLocation))
                    {
                        results.Add(new ConfigViolation {Message = "Config file does not exist", Info = setting});
                        continue;
                    }

                    xmlDoc = new XmlDocument();
                    xmlDoc.Load(fileLocation);

                    currentNsm = new XmlNamespaceManager(xmlDoc.NameTable);

                    foreach (var ns in ParseNamespace(setting.Namespace))
                        currentNsm.AddNamespace(ns.Key, ns.Value);

                    currentFile = fileLocation;
                }

                var xmlNode = xmlDoc.SelectSingleNode(setting.Setting, currentNsm);

                if (xmlNode == null)
                {
                    results.Add(new ConfigViolation { Message = "Does not exist", Info = setting });
                    continue;
                }

                var violations = DoOperation(request, setting, currentFile, xmlDoc, xmlNode);

                if ((violations != null) && (violations.Count > 0))
                    results.AddRange(violations);
            }

            return (results.Count == 0);
        }

        protected virtual IDictionary<string, string> ParseNamespace(string ns)
        {
            // format is prefix=namespace,....
            if (string.IsNullOrEmpty(ns))
                return new Dictionary<string, string>();


            var namespaces = new Dictionary<string, string>();

            var elements = ns.Split(',');

            foreach (var element in elements)
            {
                if (!element.Contains("="))
                    throw new FormatException(string.Format("Namespace '{0}' is invalid; format is prefix1=namespace1,prefix2=namespace2",
                        element));

                var nsElements = element.Split('=');

                if (nsElements.Length != 2)
                    throw new FormatException(string.Format("Namespace '{0}' is invalid; a single '=' delimiter is expected",
                        element));

                namespaces.Add(nsElements[0].Trim(), nsElements[1].Trim());
            }

            return namespaces;
        }
    }
}